from ursina import *
from ursina.prefabs.first_person_controller import FirstPersonController
import random  # 新增随机模块导入

app = Ursina(title='Minecraft: Python Edition (Release Version)')

# ==================== 资源配置系统 ====================
# 负责管理游戏纹理、图标等资源加载与验证
# 包含：
# - 纹理路径配置
# - 方块/物品/HUD图标字典定义
# - 资源加载与安全检查
TEXTURE_ROOT = 'assets/textures/'
# 新增音乐路径配置
MUSIC_ROOT = 'background_musics/'
# 原BG_MUSIC配置
BG_MUSIC = ['menu1.mp3', 'menu2.mp3', 'menu3.mp3', 'menu4.mp3']

BLOCK_TEXTURES = {
    'grass': None,
    'dirt': None,
    'stone': None,
    'bedrock': None,
    # 新增矿物纹理
    'iron': None,
    'diamond': None,
    'coal': None,
    'gold': None
}

# 在BLOCK_TEXTURES下方新增矿物统计字典
mineral_stats = {
    'diamond': {'total': 0, 'mined': 0},
    'gold': {'total': 0, 'mined': 0},
    'iron': {'total': 0, 'mined': 0},
    'coal': {'total': 0, 'mined': 0}
}

# 新增物品图标字典
ITEM_ICONS = {
    'grass': None,
    'dirt': None,
    'stone': None
}

# 在ITEM_ICONS下方新增HUD专用图标字典
HUD_ICONS = {
    'diamond': None,
    'gold': None,
    'iron': None,
    'coal': None
}


# ==================== 世界生成系统 ====================
# 实现三维世界生成算法，包含：
# - 地形分层生成（草地/泥土/基岩）
# - 矿物分布算法（按层随机分布）
# - 矿物统计系统
def generate_world():
    # 修改后的矿物配额配置
    mineral_quota = {
        'diamond': 10,   # 2-5层
        'gold': 20,      # 2-8层
        'iron': 30,      # 5-11层
        'coal': 50      # 8-11层
    }

    # 修改坐标范围为16x16x16
    candidate_positions = []
    for x in range(-8, 8):  # X轴范围扩大
        for z in range(-8, 8):  # Z轴范围扩大
            for y in range(2, 12):  # 2-11层（包含11）
                candidate_positions.append((x, y, z))
    
    # 打乱所有候选位置
    random.shuffle(candidate_positions)

    # 按矿物类型分层筛选位置（更新层级限制）
    # 修改后的分层筛选条件（调整层级范围）
    position_pools = {
        'diamond': [pos for pos in candidate_positions if 2 <= pos[1] <= 3],  # 改为2-3层
        'gold': [pos for pos in candidate_positions if 2 <= pos[1] <= 5],     # 改为2-5层
        'iron': [pos for pos in candidate_positions if 3 <= pos[1] <= 8],     # 改为3-8层
        'coal': [pos for pos in candidate_positions if 5 <= pos[1] <= 11]     # 改为5-11层
    }

    # 分配矿物到随机位置
    mineral_blocks = {}
    for mineral, quota in mineral_quota.items():
        # 从对应分层的候选位置中随机抽取
        selected = random.sample(position_pools[mineral], min(quota, len(position_pools[mineral])))
        mineral_blocks[mineral] = selected
        # 移除已选位置避免重复
        for pos in selected:
            for pool in position_pools.values():
                if pos in pool:
                    pool.remove(pos)

    # 在生成矿物后添加统计代码
    for mineral, positions in mineral_blocks.items():
        mineral_stats[mineral]['total'] = len(positions)

    # 修改后的分层生成逻辑（保持y轴范围不变）
    for x in range(-8, 8):
        for z in range(-8, 8):
            for y in range(16, -1, -1):  # 总高度16层
                if y == 16:
                    block_type = 'grass'
                elif 12 <= y <= 15:  # 12-15层为泥土
                    block_type = 'dirt'
                elif 2 <= y <= 11:  # 矿物生成范围保持2-11层
                    block_type = 'stone'
                    current_pos = (x, y, z)
                    for mineral, positions in mineral_blocks.items():
                        if current_pos in positions:
                            block_type = mineral
                            break
                elif y == 1:  # 第一层为基岩
                    block_type = 'bedrock'
                else:
                    continue
                
                Voxel((x, y, z), block_type)
# 在generate_world()末尾添加
print("矿物统计数据:", mineral_stats)

def load_resources():
    """安全加载所有资源（带调试输出）"""
    try:
        print("\n=== 正在加载方块贴图 ===")
        BLOCK_TEXTURES['grass'] = load_texture(TEXTURE_ROOT + 'blocks/grass.png')
        BLOCK_TEXTURES['dirt'] = load_texture(TEXTURE_ROOT + 'blocks/dirt.png')
        BLOCK_TEXTURES['stone'] = load_texture(TEXTURE_ROOT + 'blocks/stone.png')
        
        # 加载基岩贴图
        BLOCK_TEXTURES['bedrock'] = load_texture(TEXTURE_ROOT + 'blocks/bedrock.png')
        
        # 新增矿物贴图加载
        BLOCK_TEXTURES['iron'] = load_texture(TEXTURE_ROOT + 'blocks/iron_ore.png')
        BLOCK_TEXTURES['diamond'] = load_texture(TEXTURE_ROOT + 'blocks/diamond_ore.png')
        BLOCK_TEXTURES['coal'] = load_texture(TEXTURE_ROOT + 'blocks/coal_ore.png')
        BLOCK_TEXTURES['gold'] = load_texture(TEXTURE_ROOT + 'blocks/gold_ore.png')
        
        print("\n=== 正在加载HUD专用图标 ===")
        for mineral in ['diamond', 'gold', 'iron', 'coal']:
            HUD_ICONS[mineral] = load_texture(TEXTURE_ROOT + 'items/' + mineral + '_ore_icon.png')
            print(f"HUD图标 {mineral}: {'√' if HUD_ICONS[mineral] else '×'}")

        print("\n=== 正在加载物品图标 ===")
        # 调试输出具体文件路径
        ITEM_ICONS['grass'] = load_texture(TEXTURE_ROOT + 'items/grass_icon.png')
        ITEM_ICONS['dirt'] = load_texture(TEXTURE_ROOT + 'items/dirt_icon.png')
        ITEM_ICONS['stone'] = load_texture(TEXTURE_ROOT + 'items/stone_icon.png')
               
        print("\n=== 资源验证 ===")
        for name, tex in ITEM_ICONS.items():
            status = "√" if tex else "×"
            print(f"{name.ljust(6)}: {status} 尺寸: {tex.width}x{tex.height if tex else 'N/A'}")
            if tex and tex.width != tex.height:
                raise Exception(f"图标 {name} 不是正方形")
                
        print("\n资源加载完成！")
    except Exception as e:
        print(f"\n错误: {str(e)}")
        application.quit()

load_resources()

# ==================== 核心游戏系统 ====================
class Voxel(Entity):
    def __init__(self, position=(0,0,0), block_type='dirt'):
        super().__init__(
            parent=scene,
            position=position,
            model='cube',
            texture=BLOCK_TEXTURES[block_type],
            collider='box',
            scale=(1,1,1),
            origin=(-0.5,-0.5,-0.5),
            texture_scale=(2,2) if block_type == 'grass' else (1,1),
            name=f"block_{position}"
        )
        self.block_type = block_type
        self.default_color = color.white

    def update(self):
        if self.hovered:
            # 计算玩家与方块的直线距离
            distance = (self.position - player.position).length()
            # 仅当距离在5格以内时显示阴影
            if distance <= 5:
                self.color = color.gray
            else:
                self.color = self.default_color
        else:
            self.color = self.default_color

    def input(self, key):
        if self.hovered:
            # 添加距离检查
            distance = (self.position - player.position).length()
            max_reach = 5  # 设置最大操作距离为5个方块
            
            if distance > max_reach:
                return  # 如果超出距离则不执行任何操作
                
            if key == 'left mouse down' and self.block_type != 'bedrock':
                if self.block_type in mineral_stats:
                    mineral_stats[self.block_type]['mined'] += 1
                destroy(self)
            elif key == 'right mouse down':
                new_pos = self.position + mouse.normal
                existing_block = next((e for e in scene.entities if e.name == f"block_{new_pos}"), None)
                if not existing_block:
                    Voxel(position=new_pos, block_type=selected_block)

# ==================== 玩家控制系统 ====================
# 在玩家控制器配置处修改参数
player = FirstPersonController(
    position=(0, 17, 0),
    mouse_sensitivity=Vec2(40, 40),  # 降低鼠标灵敏度 (原80)
    jump_height=2.5,                # 减小跳跃高度 (原3)
    speed=6,                        # 降低移动速度 (原10)
    gravity=1.0,                    # 增加重力 (原0.9)
    spawn_point=(0, 17, 0)
)

# 核心机制差异
# 天空盒系统
sky_texture = load_texture('sky_sunset')
Sky(texture=sky_texture)

player.cursor.visible = False

# ==================== 物品栏系统 ====================
class Hotbar:
    def __init__(self):
        self.slots = ['grass', 'dirt', 'stone']
        self.selected_index = 0
        self.slot_size = 0.08

        # 添加半透明背景
        self.background = Entity(
            parent=camera.ui,
            model='quad',
            color=color.white33,
            scale=(0.4, 0.15),
            position=(0, -0.45),
            z=-0.7  # 确保在按钮下方
        )

        # 修改分割线创建方式，添加实例引用
        self.divider1 = Entity(
            parent=camera.ui,
            model='quad',
            color=color.black,
            scale=(0.002, 0.15),
            position=(-0.06, -0.45),
            z=-0.6
        )
        self.divider2 = Entity(
            parent=camera.ui,
            model='quad',
            color=color.black,
            scale=(0.002, 0.15),
            position=(0.06, -0.45),
            z=-0.6
        )

        positions = [-0.12, 0, 0.12]
        
        self.buttons = []
        for i, (block, pos_x) in enumerate(zip(self.slots, positions)):
            btn_texture = ITEM_ICONS[block] or load_texture('white_cube')
            btn = Button(
                parent=camera.ui,
                model=Quad(radius=0.02, segments=32),
                scale=self.slot_size,
                position=(pos_x, -0.45),
                texture=btn_texture,
                texture_scale=(1, 1),
                color=color.white,
                z=-0.5  # 按钮在分割线上方
            )
            self.buttons.append(btn)
            
        self.select(0)

    def select(self, index):
        index = max(0, min(index, len(self.slots)-1))
        for btn in self.buttons:
            btn.scale = self.slot_size
        self.buttons[index].scale = self.slot_size * 1.1
        self.selected_index = index
        global selected_block
        selected_block = self.slots[index]

hotbar = Hotbar()
selected_block = hotbar.slots[hotbar.selected_index]

# ==================== 用户界面 ====================
def create_crosshair():
    """高精度红十字准星"""
    line_cfg = {
        'parent': camera.ui,
        'model': 'quad',
        'color': color.rgb(255, 0, 0),
        'position': (0, 0, 0)
    }
    
    # 将准星实体添加到列表
    crosshair.clear()
    crosshair.extend([
        Entity(**line_cfg, scale=(0.05, 0.005)),
        Entity(**line_cfg, scale=(0.005, 0.05))
    ])

# 在HUD创建函数中添加强制刷新
def create_mineral_hud():
    # 移除重置已挖数量的代码（导致统计被覆盖）
    y_offset = 0.47
    
    # 先清空再添加新元素
    mineral_hud.clear()

    # 背景板z值调整为-0.7（与物品栏背景一致）
    bg = Entity(
        parent=camera.ui,
        model='quad',
        color=color.white33,
        scale=(0.3, 0.4),
        position=(-0.73, 0.32),
        z=-0.7  # 调整为与物品栏背景相同层级
    )
    mineral_hud.append(bg)

    # 图标和文本保持原有z值-0.5（在背景之上）
    for mineral in ['diamond', 'gold', 'iron', 'coal']:
        icon = Entity(
            parent=camera.ui,
            model=Quad(radius=0, segments=4),
            texture=HUD_ICONS[mineral],
            scale=0.065,
            position=(-0.78, y_offset),  # 微调垂直位置
            origin=(-0.5, 0.5),
            texture_scale=(1,1),
            color=color.white,
            z=-0.5  # 保持原有值
        )

        text_entity = Text(
            parent=camera.ui,
            position=(-0.68, y_offset - 0.01),  # 微调垂直对齐
            size=0.03,  # 进一步缩小文字尺寸
            origin=(-0.5, 0.5),  # 恢复垂直居中
            color=color.white,
            text=f"{mineral_stats[mineral]['mined']}/{mineral_stats[mineral]['total']}",
            tag=f'mineral_{mineral}',
            scale=(1,1)
        )
        
        mineral_hud.extend([icon, text_entity])
        y_offset -= 0.085  # 减小垂直间距
    
# 保留并合并最终的update函数（文件末尾）
def update():
    if player.y < -5:
        player.position = player.spawn_point
    music_manager.update()
    
    # 合并HUD更新逻辑
    for mineral in ['diamond', 'gold', 'iron', 'coal']:
        text_entities = [e for e in mineral_hud 
                       if isinstance(e, Text) and e.tag == f'mineral_{mineral}']
        if text_entities:
            text_entities[0].text = f"{mineral_stats[mineral]['mined']}/{mineral_stats[mineral]['total']}"

# ==================== 输入控制 ====================
def input(key):
    # 原有按键处理
    if key in {'1','2','3'}:
        hotbar.select(int(key)-1)
    if key == 'scroll up':
        hotbar.select(hotbar.selected_index - 1)
    elif key == 'scroll down':
        hotbar.select(hotbar.selected_index + 1)
    
    # 新增音乐切换功能
    if key == 'm':
        if music_manager.current_song:
            music_manager.current_song.stop()
        music_manager.play_next()
    
    global selected_block
    selected_block = hotbar.slots[hotbar.selected_index]

    if key == 'f1':
        # 统一切换所有UI元素
        for element in [*crosshair, *mineral_hud, 
                       hotbar.background, hotbar.divider1, 
                       hotbar.divider2, *hotbar.buttons]:
            element.visible = not element.visible

    if key == 'escape':
        application.quit()

# 在创建HUD时记录元素 ================
crosshair = []
mineral_hud = []

create_crosshair()

# ==================== 游戏配置 ====================
window.title = 'Minecraft: Python Edition (Release Version)'
window.borderless = False
window.fullscreen = False

# ==================== 音乐管理系统 ====================
class MusicManager:
    def __init__(self):
        self.playlist = BG_MUSIC.copy()
        self.current_index = -1
        self.current_song = None
        random.shuffle(self.playlist)  # 初始随机排序
        
    def update(self):
        if not self.current_song or not self.current_song.playing:
            self.play_next()
            
    def play_next(self):
        self.current_index = (self.current_index + 1) % len(self.playlist)
        song_path = MUSIC_ROOT + self.playlist[self.current_index]
        self.current_song = Audio(song_path, loop=False, autoplay=True)

# 在玩家控制器初始化前添加音乐管理器实例
music_manager = MusicManager()

# 修改主程序入口代码
# 在文件末尾的main代码块中，修改HUD初始化逻辑
if __name__ == '__main__':
    if hasattr(sys, '_MEIPASS'):
        TEXTURE_ROOT = path.join(sys._MEIPASS, TEXTURE_ROOT)
        MUSIC_ROOT = path.join(sys._MEIPASS, MUSIC_ROOT)
    
    generate_world()
    # 在游戏启动时创建HUD（原调用位置被覆盖）
    create_mineral_hud()
    music_manager.play_next()
    
    # 确保只有一个update函数
    def update():
        if player.y < -5:
            player.position = player.spawn_point
        music_manager.update()
        
        # 强制更新HUD显示
        for mineral in ['diamond', 'gold', 'iron', 'coal']:
            text_entities = [e for e in mineral_hud 
                           if isinstance(e, Text) and e.tag == f'mineral_{mineral}']
            if text_entities:
                text_entities[0].text = f"{mineral_stats[mineral]['mined']}/{mineral_stats[mineral]['total']}"
    
    window.fps_counter.enabled = True
    window.vsync = True
    app.run()